 /*******************************************************************************
  * Copyright (c) 2003, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.navigator;

 import java.util.Arrays ;
 import java.util.Iterator ;
 import java.util.List ;

 import org.eclipse.jface.viewers.DecoratingLabelProvider;
 import org.eclipse.jface.viewers.IBaseLabelProvider;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.jface.viewers.ViewerSorter;
 import org.eclipse.swt.dnd.DND;
 import org.eclipse.swt.events.DisposeEvent;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Widget;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.navigator.ContributorTrackingSet;
 import org.eclipse.ui.internal.navigator.NavigatorContentService;
 import org.eclipse.ui.internal.navigator.NavigatorPipelineService;

 /**
  *
  * Provides the Tree Viewer for the Common Navigator. Content and labels are
  * provided by an instance of {@link INavigatorContentService}&nbsp; which uses
  * the ID supplied in the constructor
  * {@link CommonViewer#CommonViewer(String, Composite, int)} or through
  * {@link NavigatorContentServiceFactory#createContentService(String, org.eclipse.jface.viewers.StructuredViewer)}.
  *
  * <p>
  * Clients may extend this class.
  * </p>
  *
  * <p>
  * Note that as of 3.2.1 and 3.3, the common viewer caches its selection.
  * Clients must not set the selection of the viewer's tree control directly.
  * </p>
  *
  * @since 3.2
  */
 public class CommonViewer extends TreeViewer {

     private final NavigatorContentService contentService;

     private ISelection cachedSelection;
     
     /**
      * <p>
      * Constructs the Tree Viewer for the Common Navigator and the corresponding
      * NavigatorContentService. The NavigatorContentService will provide the
      * Content Provider and Label Provider -- these need not be supplied by
      * clients.
      * <p>
      * For the valid bits to supply in the style mask (aStyle), see
      * documentation provided by {@link TreeViewer}.
      * </p>
      *
      * @param aViewerId
      * An id tied to the extensions that is used to focus specific
      * content to a particular instance of the Common Navigator
      * @param aParent
      * A Composite parent to contain the actual SWT widget
      * @param aStyle
      * A style mask that will be used to create the TreeViewer
      * Composite.
      */
     public CommonViewer(String aViewerId, Composite aParent, int aStyle) {
         super(aParent, aStyle);
         contentService = new NavigatorContentService(aViewerId, this);
         init();
     }

     /**
      * <p>
      * Initializes the content provider, label provider, and drag and drop
      * support. Should not be called by clients -- this method is invoked when
      * the constructor is invoked.
      * </p>
      */
     protected void init() {
         setUseHashlookup(true);
         setContentProvider(contentService.createCommonContentProvider());
         DecoratingLabelProvider decoratingProvider = new DecoratingLabelProvider(
                 contentService.createCommonLabelProvider(), PlatformUI
                         .getWorkbench().getDecoratorManager()
                         .getLabelDecorator());
         setLabelProvider(decoratingProvider);
         initDragAndDrop();

     }

     protected void removeWithoutRefresh(Object [] elements) {
         super.remove(elements);
     }

     /**
      * <p>
      * Adds DND support to the Navigator. Uses hooks into the extensible
      * framework for DND.
      * </p>
      * <p>
      * By default, the following Transfer types are supported:
      * <ul>
      * <li>LocalSelectionTransfer.getInstance(),
      * <li>PluginTransfer.getInstance()
      * </ul>
      * </p>
      *
      * @see CommonDragAdapter
      * @see CommonDropAdapter
      */
     protected void initDragAndDrop() {

         /* Handle Drag and Drop */
         int operations = DND.DROP_COPY | DND.DROP_MOVE;

         CommonDragAdapter dragAdapter = new CommonDragAdapter(contentService,
                 this);
         addDragSupport(operations, dragAdapter.getSupportedDragTransfers(),
                 dragAdapter);

         CommonDropAdapter dropAdapter = new CommonDropAdapter(contentService,
                 this);
         addDropSupport(operations, dropAdapter.getSupportedDropTransfers(),
                 dropAdapter);

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.AbstractTreeViewer#createTreeItem(org.eclipse.swt.widgets.Widget,
      * java.lang.Object, int)
      */
     protected void createTreeItem(Widget parent, final Object element, int index) {
         try {
             super.createTreeItem(parent, element, index);
         } catch (Exception ex) {
             ex.printStackTrace();
         } catch (Error e) {
             e.printStackTrace();
         }

     }

     /*
      * @see ContentViewer#handleLabelProviderChanged(LabelProviderChangedEvent)
      */
     protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {

         Object [] changed = event.getElements();
         if (changed != null) {
             List others = Arrays.asList(changed);
             for (Iterator iter = others.iterator(); iter.hasNext();) {
                 if(iter.next() == null)
                     iter.remove();
             }
             if (others.isEmpty()) {
                 return;
             }
             event = new LabelProviderChangedEvent((IBaseLabelProvider) event
                     .getSource(), others.toArray());
         }
         super.handleLabelProviderChanged(event);
     }

     protected void handleDispose(DisposeEvent event) {
         dispose();
         super.handleDispose(event);
     }
  
     /**
      * <p>
      * Disposes of the NavigatorContentService, which will dispose the Content
      * and Label providers.
      * </p>
      */
     public void dispose() {
         if (contentService != null) {
             contentService.dispose();
         }
         clearSelectionCache();
     }

     /**
      * Sets this viewer's sorter and triggers refiltering and resorting of this
      * viewer's element. Passing <code>null</code> turns sorting off.
      *
      * @param sorter
      * a viewer sorter, or <code>null</code> if none
      */
     public void setSorter(ViewerSorter sorter) {
         if (sorter != null && sorter instanceof CommonViewerSorter) {
             ((CommonViewerSorter) sorter).setContentService(contentService);
         }

         super.setSorter(sorter);
     }

     /**
      * <p>
      * The {@link INavigatorContentService}provides the hook into the framework
      * to provide content from the various extensions.
      * </p>
      *
      * @return The {@link INavigatorContentService}that was created when the
      * viewer was created.
      */
     public INavigatorContentService getNavigatorContentService() {
         return contentService;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.AbstractTreeViewer#add(java.lang.Object,
      * java.lang.Object[])
      */
     public void add(Object parentElement, Object [] childElements) {
         // TODO Intercept ADD for the pipeline service.

         NavigatorPipelineService pipeDream = (NavigatorPipelineService) contentService
                 .getPipelineService();

         PipelinedShapeModification modification = new PipelinedShapeModification(
                 parentElement, new ContributorTrackingSet(contentService,
                         childElements));

         pipeDream.interceptAdd(modification);

         Object parent = (parentElement == getInput()) ? getInput()
                 : modification.getParent();

         super.add(parent, modification.getChildren().toArray());

     }

     /**
      * <p>
      * Removals are handled by refreshing the parents of each of the given
      * elements. The parents are determined via calls ot the contentProvider.
      * </p>
      *
      * @see org.eclipse.jface.viewers.AbstractTreeViewer#remove(java.lang.Object[])
      */
     public void remove(Object [] elements) {

         // TODO Intercept REMOVE for the pipeline service.

         NavigatorPipelineService pipeDream = (NavigatorPipelineService) contentService
                 .getPipelineService();

         PipelinedShapeModification modification = new PipelinedShapeModification(
                 null, new ContributorTrackingSet(contentService, elements));

         pipeDream.interceptRemove(modification);

         super.remove(modification.getChildren().toArray());
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#refresh(java.lang.Object,
      * boolean)
      */
     public void refresh(Object element, boolean updateLabels) {

         
         if(element != getInput()) {
         
             INavigatorPipelineService pipeDream = contentService
                     .getPipelineService();
     
             PipelinedViewerUpdate update = new PipelinedViewerUpdate();
             update.getRefreshTargets().add(element);
             update.setUpdateLabels(updateLabels);
             /* if the update is modified */
             if (pipeDream.interceptRefresh(update)) {
                 /* intercept and apply the update */
                 boolean toUpdateLabels = update.isUpdateLabels();
                 for (Iterator iter = update.getRefreshTargets().iterator(); iter
                         .hasNext();) {
                     super.refresh(iter.next(), toUpdateLabels);
                 }
             } else {
                 super.refresh(element, updateLabels);
             }
         } else {
             super.refresh(element, updateLabels);
         }
     }
     
     /*
      * (non-Javadoc)
      * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean)
      */
     public void setSelection(ISelection selection, boolean reveal) {

         if(selection instanceof IStructuredSelection) {
             IStructuredSelection sSelection = (IStructuredSelection) selection;
             
             INavigatorPipelineService pipeDream = contentService
                     .getPipelineService();

             PipelinedViewerUpdate update = new PipelinedViewerUpdate();
             update.getRefreshTargets().addAll(sSelection.toList());
             update.setUpdateLabels(false);
             /* if the update is modified */
             if (pipeDream.interceptRefresh(update)) {
                 /* intercept and apply the update */
                 super.setSelection(new StructuredSelection(update.getRefreshTargets().toArray()) , reveal);
             } else {
                 super.setSelection(selection, reveal);
             }
         }
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.jface.viewers.AbstractTreeViewer#setSelectionToWidget(java.util.List, boolean)
      */
     protected void setSelectionToWidget(List v, boolean reveal) {
         clearSelectionCache();
         super.setSelectionToWidget(v, reveal);
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.jface.viewers.AbstractTreeViewer#handleDoubleSelect(org.eclipse.swt.events.SelectionEvent)
      */
     protected void handleDoubleSelect(SelectionEvent event) {
         clearSelectionCache();
         super.handleDoubleSelect(event);
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.jface.viewers.StructuredViewer#handleOpen(org.eclipse.swt.events.SelectionEvent)
      */
     protected void handleOpen(SelectionEvent event) {
         clearSelectionCache();
         super.handleOpen(event);
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.jface.viewers.StructuredViewer#handlePostSelect(org.eclipse.swt.events.SelectionEvent)
      */
     protected void handlePostSelect(SelectionEvent e) {
         clearSelectionCache();
         super.handlePostSelect(e);
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.jface.viewers.StructuredViewer#handleSelect(org.eclipse.swt.events.SelectionEvent)
      */
     protected void handleSelect(SelectionEvent event) {
         clearSelectionCache();
         super.handleSelect(event);
     }
     
     /**
      * Clears the selection cache.
      */
     private void clearSelectionCache() {
         cachedSelection = null;
     }
     
     /**
      * Returns the current selection.
      * <p>
      * Note that as of 3.2.1 and 3.3, the common viewer caches its selection.
      * Clients must not set the selection of the viewer's tree control directly.
      * </p>
      *
      * @see org.eclipse.jface.viewers.AbstractTreeViewer#getSelection()
      */
     public ISelection getSelection() {
         if (cachedSelection == null) {
             cachedSelection = super.getSelection();
         }
         return cachedSelection;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#refresh(java.lang.Object)
      */
     public void refresh(Object element) {
         refresh(element, true);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.StructuredViewer#update(java.lang.Object,
      * java.lang.String[])
      */
     public void update(Object element, String [] properties) {


         if(element != getInput()) {
             INavigatorPipelineService pipeDream = contentService
                     .getPipelineService();
     
             PipelinedViewerUpdate update = new PipelinedViewerUpdate();
             update.getRefreshTargets().add(element);
             update.setUpdateLabels(true);
             /* if the update is modified */
             if (pipeDream.interceptUpdate(update)) {
                 /* intercept and apply the update */
                 for (Iterator iter = update.getRefreshTargets().iterator(); iter
                         .hasNext();) {
                     super.update(iter.next(), properties);
                 }
             } else {
                 super.update(element, properties);
             }
         } else {
             super.update(element, properties);
         }
     }

     /*
      * (non-Javadoc)
      *
      * @see java.lang.Object#toString()
      */
     public String toString() {
         return contentService.toString() + " Viewer"; //$NON-NLS-1$
 }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefresh(java.lang.Object,
      * boolean)
      */
     protected void internalRefresh(Object element, boolean updateLabels) {
         if (element == null && getRoot() == null) {
             return;
         }
         super.internalRefresh(element, updateLabels);
     }

 }

